<template>
  <div>
    <el-upload
      v-if="!disabled"
      ref="uploadRef"
      :action="action"
      :show-file-list="false"
      :disabled="disabled || loading"
      :accept="accept"
      :limit="limit"
      :on-progress="handleUploadProgress"
      :on-success="handleUploadSuccess"
      :on-error="handleUploadError"
      :on-remove="handleUploadRemove"
      :before-upload="handleBeforeUpload"
    >
      <el-button
        type="primary"
        :size="size"
        :disabled="disabled || loading || (limit && fileList?.length === limit)"
      >
        点击上传
      </el-button>
    </el-upload>
    <div
      class="ml-2 flex items-center"
      v-for="(file, index) in fileList"
      :key="index"
    >
      <div class="w-56 break-all">
        <el-link
          :disabled="file.percentage !== 100"
          :loading="downloadFileLoading"
          @click="handleDownloadFile(file)"
        >
          {{ file.name }}
        </el-link>
      </div>
      <el-progress
        class="ml-2 w-56"
        v-if="file.percentage !== 100"
        :percentage="file.percentage"
      />
      <el-icon v-if="!disabled && file.percentage === 100" class="ml-2">
        <CircleClose
          class="cursor-pointer"
          @click="handleBeforeRemoveFileList(file)"
        />
      </el-icon>
    </div>
    <div v-if="(!fileList || fileList?.length === 0) && !disabled" class="ml-2">
      支持的文件类型为{{ showAccept }}, 单个文件大小不得超过 50MB
    </div>
  </div>
</template>

<script setup lang="ts">
import {
  UploadFile,
  UploadFiles,
  UploadInstance,
  UploadProgressEvent,
} from 'element-plus'
import { computed, nextTick, onUnmounted, ref, useTemplateRef } from 'vue'
import { CircleClose } from '@element-plus/icons-vue'
import { Result } from '@/plugins/axios'
import { IFileObj } from '@/type/file'
import { cloneDeep } from 'lodash-es'
import useFetch from '@/hooks/useFetch'
import { getFileStreamById } from '@/api/file'
import { downloadFile, getFileExt } from '@/plugins/file'
import { Message } from '@/hooks/message'

const {
  loading: downloadFileLoading,
  execute: downloadFileExecute,
  onFetchSuccess: downloadFileOnFetchSuccess,
} = useFetch(getFileStreamById)

downloadFileOnFetchSuccess((data: Blob, fileName: string) => {
  downloadFile(data, fileName)
})

const uploadRef = useTemplateRef<UploadInstance>('uploadRef')

interface IFileData {
  id?: number
  url?: string
  fileName?: string
  orgFileName?: string
  createTime?: string
  updateTime?: string
  state?: number
  businessType?: number
  obsUrl?: string
  fileSize?: number
  createUserId?: number
  businessId?: number
}

const {
  action = '/supervision-api/fileController/fileUpload',
  accept = '.doc,.docx,.pdf,.xlsx,.xls,.png,.jpg,.jpeg,.ppt,.pptx',
  size = 'small',
  disabled = false,
  limit,
  maxSize = 50,
} = defineProps<{
  action?: string
  baseFileUrl?: string
  delAction?: string
  accept?: string
  size?: 'large' | 'default' | 'small'
  disabled?: boolean
  limit?: number
  maxSize?: number
}>()

const fileList = defineModel<IFileObj[]>()

const loading = ref(false)

const showAccept = computed(() => {
  if (!accept) return ''
  return accept.replace(/\./g, '')
})

function duplicate(arr: IFileObj[]) {
  if (!arr?.length) return []
  let newArr = []
  let map = {}
  arr.forEach((item) => {
    if (!item.uid) {
      newArr.push(item)
      return
    }
    if (!map[item.uid]) {
      newArr.push(item)
    } else {
      // 更新item
      const curIndex = newArr.findIndex((child) => child.uid === item.uid)
      if (curIndex !== -1) {
        newArr.splice(curIndex, 1, item)
      }
    }
    map[item.uid] = item
  })
  return newArr
}

function concaIFileObjList(uploadFiles: UploadFiles) {
  if (fileList.value?.length) {
    const newFiles = duplicate([...uploadFiles, ...fileList.value])
    fileList.value = newFiles
  } else {
    fileList.value = uploadFiles
  }
}

function handleUploadProgress(
  evt: UploadProgressEvent,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles,
) {
  loading.value = true
  concaIFileObjList(uploadFiles)
}

// 上传前
function handleBeforeUpload(file: UploadFile) {
  console.log(file, 'file')
  const { size, name } = file
  // 校验文件类型
  const extension = getFileExt(name)
  const accepts = accept.split(',')
  if (!accepts.includes(extension)) {
    Message({
      message: `不支持 ${extension} 格式的文件，请重新上传。`,
      type: 'error',
    })
    return false
  }

  if (size > maxSize * 1024 * 1024) {
    Message({
      message: `${name} 文件不能超过 ${maxSize}MB，请重新上传。`,
      type: 'error',
    })
    return false
  }
  const isExit = fileList.value?.some((item) => item.name === name)
  if (isExit) {
    Message({
      message: `${name} 文件已存在，请重新上传。`,
      type: 'error',
    })
    return false
  }
}

// 更新文件信息
function updateCurFile(data: IFileData) {
  const id = data.id
  if (!id) return
  fileList.value = fileList.value.map((file: IFileObj) => {
    const fileId = getFileId(file)
    if (fileId === id) {
      return {
        ...file,
        id: fileId,
        name: data?.orgFileName,
      }
    }
    return file
  })
}

// 获取文件id
function getFileId(file: IFileObj) {
  return file?.response?.data?.id
}

// 上传失败后根据uid删除文件
function deleteCurFile(file: IFileObj) {
  uploadRef?.value.handleRemove(file)
  fileList.value = fileList.value.filter(
    (fileItem: IFileObj) => fileItem.uid !== file.uid,
  )
}

async function handleUploadSuccess(data: Result<IFileData>, file: IFileObj) {
  if (data.status === 200) {
    await nextTick()
    updateCurFile(data.data)
  } else {
    Message({
      message: data.message,
      type: 'error',
    })
    deleteCurFile(file)
  }
  loading.value = false
}

function handleUploadError(
  error: Error,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles,
) {
  Message({
    message: `${uploadFile.name} 上传失败，请重新上传。`,
    type: 'error',
  })
  concaIFileObjList(uploadFiles)
  loading.value = false
}

function handleUploadRemove(uploadFile: UploadFile, uploadFiles: UploadFiles) {
  concaIFileObjList(uploadFiles)
  loading.value = false
}

function handleBeforeRemoveFileList(file: IFileObj) {
  if (disabled) {
    return
  }
  removeFile(file)
}

// 根据id删除文件
async function removeFile(file: IFileObj) {
  let id = file?.id
  if (!id) return
  if (file?.response) {
    uploadRef?.value.handleRemove(file)
  }
  const oldFiles = cloneDeep(fileList.value)
  const newFiles = oldFiles.filter((file) => {
    const curId = file?.id || file?.response?.data?.id
    return curId !== id
  })
  fileList.value = newFiles
}

defineExpose({ clearFileList })

function clearFileList() {
  fileList.value = []
  loading.value = false
  uploadRef?.value?.clearFiles()
}

onUnmounted(() => {
  clearFileList()
})

// 下载文件
async function handleDownloadFile(file: IFileObj) {
  await downloadFileExecute<number, string>(file.id, true, file.name)
}
</script>

<style scoped lang="scss">
.text-only {
  display: inline-block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style>
